package com.ruicar.afs.cloud.gateway.filter.afs;

import com.ruicar.afs.cloud.common.core.constant.CommonConstants;
import com.ruicar.afs.cloud.common.core.util.ClientTypeUtil;
import com.ruicar.afs.cloud.common.gateway.filters.AfsGatewayFilter;
import com.ruicar.afs.cloud.common.gateway.support.NacosRouterDefinitionWriter;
import com.ruicar.afs.cloud.gateway.components.OpenApiAccessToken;
import com.ruicar.afs.cloud.gateway.util.AfsGateWayHelper;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;

import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR;

@Component
@AllArgsConstructor
@Slf4j
public class AfsOpenApiGatewayFilter implements AfsGatewayFilter {
    private static final String CLIENT_ID  = "clientId";
    private static final String CLIENT_SECRET  = "clientSecret";
    private final OpenApiAccessToken openApiAccessToken;
    private final NacosRouterDefinitionWriter nacosRouterDefinitionWriter;
    private static final Set<String> DEFAULT_CLIENTS=new HashSet<>();
    private static final AntPathMatcher pathMatcher = new AntPathMatcher();
    static {
        Arrays.stream(ClientTypeUtil.ClientTypeEnum.values())
                .forEach(type->{
                    DEFAULT_CLIENTS.add(type.getClientId().toUpperCase());
                });
    }

    @Override
    public String processorName() {
        return "open-api";
    }

    @Override
    public String matchPattern() {
        return "/openapi/**/*";
    }

    @Override
    public ServerHttpRequest.Builder processRequest(ServerWebExchange exchange) {
        ServerHttpRequest request = exchange.getRequest().mutate()
                .headers(httpHeaders -> {
                })
                .build();
        String clientId = AfsGateWayHelper.getHeaderValue(exchange,CLIENT_ID);
        String clientSecret = AfsGateWayHelper.getHeaderValue(exchange,CLIENT_SECRET);

        Assert.isTrue((!StringUtils.isEmpty(clientId)&&!StringUtils.isEmpty(clientSecret)),"clientId或clientSecret为空");

        Assert.isTrue(!DEFAULT_CLIENTS.contains(clientId.toUpperCase()),"clientId错误");

        String accessToken = openApiAccessToken.getAccessToken(clientId,clientSecret);
        String uriPath = "/" + Arrays.stream(StringUtils.tokenizeToStringArray(request.getURI().getRawPath(), "/")).skip(2L).collect(Collectors.joining("/"));
        String newUriPath = "/" + Arrays.stream(StringUtils.tokenizeToStringArray(request.getURI().getRawPath(), "/")).skip(1L).collect(Collectors.joining("/"));
        ServerHttpRequest.Builder builder = request.mutate().path(uriPath);
        builder.header(CommonConstants.AUTHORIZATION, CommonConstants.BEARER + accessToken);
        builder.header(CommonConstants.TENANT_ID,CommonConstants.BASE_TENANT_ID);
        builder.header(CommonConstants.ACCESS_TOKEN, accessToken);
        exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR,uriPath);
        String routerKey = nacosRouterDefinitionWriter.ROUTES.keySet()
                .stream()
                .filter(key-> pathMatcher.match(key,newUriPath))
                .findFirst().orElse("null");
        if(nacosRouterDefinitionWriter.ROUTES.get(routerKey)==null){
            throw new RuntimeException("找不到资源");
        }
        Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
        Route newRoute = Route.async()
                .asyncPredicate(route.getPredicate())
                .filters(route.getFilters())
                .id(route.getId())
                .order(route.getOrder())
                .uri(nacosRouterDefinitionWriter.ROUTES.get(routerKey))
                .build();
        exchange.getAttributes().put(GATEWAY_ROUTE_ATTR,newRoute);
        return builder;
    }

    @Override
    public int getOrder() {
        return 0;
    }
}
